function PDMatrix() {
    this.a = 1;//float
    this.b = 0; //float
    this.c = 0; //float
    this.d = 1; //float
    this.e = 0; //float
    this.f = 0; //float

    this.Set = function (a, b, c, d, e, f) {
        this.a = a;
        this.b = b;
        this.c = c;
        this.d = d;
        this.e = e;
        this.f = f;
    }

    this.Clon = function(mt)
    {
    	this.a = mt.a;
        this.b = mt.b;
        this.c = mt.c;
        this.d = mt.d;
        this.e = mt.e;
        this.f = mt.f;
    }
    this.Reset = function () {
        this.a = this.d = 1;
        this.b = this.c = this.e = this.f = 0;
    }

    this.SetReverse = function (mt) {
        var i = mt.a * mt.d - mt.b * mt.c;
	    if (i  == 0) return;
	    var j = -i;
	    this.a = mt.d / i;
	    this.b = mt.b / j;
	    this.c = mt.c / j;
	    this.d = mt.a / i;
	    this.e = (mt.c * mt.f - mt.d * mt.e) / i;
	    this.f = (mt.a * mt.f - mt.b * mt.e) / j;
    }

    
    this.Rotate = function (fRadian, bPrepended) {
        var cosValue = Math.cos(fRadian);
        var sinValue = Math.sin(fRadian);
        var m = new PDMatrix();
        m.Set(cosValue, sinValue, -sinValue, cosValue, 0, 0);
        var rel = null;
        if (bPrepended)
            rel = Concat2mt(m, this);
        else
            rel = Concat2mt(this, m);
        this.Set(rel.a, rel.b, rel.c, rel.d, rel.e, rel.f);

    }
    this.RotateAt = function (dx, dy, fRadian, bPrepended) {
        this.Translate(dx, dy, bPrepended);
        this.Rotate(fRadian, bPrepended);
        this.Translate(-dx, -dy, bPrepended);
    }
    this.Concat = function (a, b, c, d, e, f, bPrepended) {
        var m = new PDMatrix();
        m.Set(a, b,c,d,e,f); 
        var rel = null;
        if (bPrepended) {
            rel = PDMatrix.prototype.Concat2mt(m, this);
         }
         else{
             rel = PDMatrix.prototype.Concat2mt(this, m);
         }
         this.Set(rel.a, rel.b, rel.c, rel.d, rel.e, rel.f);

    }
    //is 90 degree or -90 degree rotated
    this.Is90Rotated = function () {
        var a1 = this.a * 1000;
        if (a1 < 0) a1 *= -1;
        var d1 = this.d * 1000;
        if (d1 < 0) d1 *= -1;
        var b1 = this.b;
        if (b1 < 0) b1 *= -1;
        var c1 = this.c;
        if (c1 < 0) c1 *= -1;
        return (a1 < b1) && (d1 < c1);

    }

    this.IsScaled = function () {
        var b1 = this.b * 1000;
        if (b1 < 0) b1 *= -1;
        var c1 = this.c * 1000;
        if (c1 < 0) c1 *= -1;
        var a1 = this.a;
        if (a1 < 0) a1 *= -1;
        var d1 = this.d;
        if (d1 < 0) d1 *= -1;
        return (b1 < a1) && (c1 < d1);
    }

    this.GetA = function () { return this.a; }
    this.GetB = function () { return this.b; }
    this.GetC = function () { return this.c; }
    this.GetD = function () { return this.d; }
    this.GetE = function () { return this.e; }
    this.GetF = function () { return this.f; }

    this.Translate = function (x, y, bPrepended) { 
        if(bPrepended) {
            this.e += x * this.a + y * this.c;
            this.f += y * this.d + x * this.b;
        }
        else
            this.e += x, this.f += y;

    }

    this.Scale = function (sx, sy, bPrepended) {
        this.a *= sx, this.d *= sy;
        if (bPrepended) {
            this.b *= sx;
            this.c *= sy;
        }
        else {
            this.b *= sy;
            this.c *= sx;
            this.e *= sx;
            this.f *= sy;
        }
    }
    this.TransformXDistance = function (dx) {
        var fx = this.a * dx;
        var fy = this.b * dx;
        return Math.sqrt(fx * fx + fy * fy);
    }

    this.TransformYDistance = function (dy) {
        var fx = this.c * dy;
        var fy = this.d * dy;
        return Math.sqrt(fx * fx + fy * fy);
    }

    this.TransformDistance = function (dx, dy) {
        var fx = this.a * dx + this.c * dy;
        var fy = this.b * dx + this.d * dy;
        return Math.sqrt(fx * fx + fy * fy);
    }
    this.TransFormPoint = function (x, y) {
        var fx = this.a * x + this.c * y + this.e;
        var fy = this.b * x + this.d * y + this.f;
        var rel = [];
        rel[0] = parseFloat(fx.toFixed(3)); rel[1] = parseFloat(fy.toFixed(3));
        return rel;
    }
    this.TransFormRect = function (left, top, right, bottom) {
        var transform = function (l, r, t, b) {
            var x = [], y = [];
            x[0] = l; y[0] = t;
            x[1] = l; y[1] = b;
            x[2] = r; y[2] = t;
            x[3] = r; y[3] = b;
            var point;
            for (var i = 0; i < 4; i++) {
                point = this.TransFormPoint(x[i], y[i]);
                x[i] = point[0]; y[i] = point[1];
            }
            point = null;
            // rel[0]: left  rel[1]: right
            // rel[2]: top   rel[3]: bottom
            var rel = [x[0], x[0], y[0], y[0]];
            for (i = 1; i < 4; i++) {
                if (rel[1] < x[i]) rel[1] = x[i];
                if (rel[0] > x[i]) rel[0] = x[i];
                
                if (rel[2] < y[i]) rel[2] = y[i];
                if (rel[3] > y[i]) rel[3] = y[i];
            }
            return rel;
        }

        var temp = transform.call(this, left, right, top, bottom);
        var rst = [];
        rst[0] = temp[0]; rst[1] = temp[2];
        rst[2] = temp[1]; rst[3] = temp[3];
        temp = null;
        transform = null;

        // rst[0]: left    rst[1]: top
        // rst[2]: right   rst[3]: bottom
        return rst;
    }
	this.GetXUnit = function () {
		if (b===0) {
			return Math.abs(a);
		}
		else if (a===0) {
			return Math.abs(b);
		}
		else {
			return Math.sqrt(a*a+b*b);
		}
	}
	this.GetYUnit = function () {
		if (c===0) {
			return Math.abs(d);
		}
		else if (d===0) {
			return Math.abs(c);
		}
		else {
			return Math.sqrt(c*c+d*d);
		}
	}
	this.GetUnitRect = function () {
		return this.TransFormRect(0, 0, 1, 1);
    }
    this.MatchRect = function (dest, src) {
        var fDiff = src[0] - src[2];
	    this.a = Math.abs(fDiff) < 0.001 ? 1 : (dest[0] - dest[2]) / fDiff;
	    fDiff = src[3] - src[1];
	    this.d = Math.abs(fDiff) < 0.001 ? 1 : (dest[3] - dest[1]) / fDiff;
	    this.e = dest[0] - src[0] * this.a;
	    this.f = dest[3] - src[3] * this.d;

	    this.b = 0;
	    this.c = 0;
    }

    PDMatrix.prototype.Concat2mt=function(m1, m2){
        var aa = m1.a * m2.a + m1.b * m2.c;
	    var bb = m1.a * m2.b + m1.b * m2.d;
	    var cc = m1.c * m2.a + m1.d * m2.c;
	    var dd = m1.c * m2.b + m1.d * m2.d;
	    var ee = m1.e * m2.a + m1.f * m2.c + m2.e;
	    var ff = m1.e * m2.b + m1.f * m2.d + m2.f;
        var m = new PDMatrix();
        m.Set(aa, bb, cc, dd, ee, ff);
	    return m;

    }
}
